package cn.ycc1.my.rag.service.impl;

import cn.ycc1.my.rag.service.ChatRagService;
//import com.alibaba.dashscope.embeddings.TextEmbedding;
import com.alibaba.dashscope.exception.NoApiKeyException;
import dev.langchain4j.model.chat.ChatLanguageModel;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.dashscope.embeddings.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * DeepSeek RAG 服务实现类
 * @author ycc
 * @date 2025/4/20
 * 《闲走》是一首描绘自然风光和心情的小诗，通过“风吹大观湖”“静享湿地风”等意象，展现了诗人在湖边漫步时的惬意与放松，暂时抛却愁绪，享受自然之美的情景。
 * 调用阿里百炼的embedding服务，将用户问题向量化，并与知识库中的文档进行相似度检索。
 * 将知识库文档请求阿里百炼的向量化模型，把结果追加保存在本地csv文件/redis/向量数据库中。
 */
@Service
public class ChatRagServiceImpl implements ChatRagService {
    @Autowired
    ChatLanguageModel model;

    // 假设我们有一些预加载的知识文档
    private List<String> knowledgeDocuments = new ArrayList<>();

    // 阿里百炼的embedding客户端
    private TextEmbedding embedding;

    // 初始化方法，加载知识库和embedding客户端
    @PostConstruct
    public void init() throws NoApiKeyException {
        // 初始化embedding客户端
        embedding = new TextEmbedding();

        // 加载知识文档 (实际应用中可以从数据库或文件加载)
        knowledgeDocuments.add("《白云山》白云山，清风拂亭晚。劳逸结合养习惯，积极进取生经验。生活入自然。");
        knowledgeDocuments.add("《闲走》风吹大观湖，心中惬意生。愁云暂散去，静享湿地风。");
        knowledgeDocuments.add("《游流溪》林荫露天日，烟雨沐流溪。曲径通幽路，处处听流笛。");
    }

    /**
     * 处理用户问题，结合知识库生成回答
     * @param question
     * @return
     */
    @Override
    public String localEmbedding(String question) {
        try {
            // 1. 将用户问题向量化
            TextEmbeddingParam param = TextEmbeddingParam
                    .builder()
                    .model(TextEmbedding.Models.TEXT_EMBEDDING_V3)
                    .texts(Arrays.asList(question)).build();
            TextEmbedding textEmbedding = new TextEmbedding();
            TextEmbeddingResult queryEmbedding = textEmbedding.call(param);

            // 2. 检索最相关的文档 (简化版，实际应用中应使用向量数据库)
            String mostRelevantDoc = retrieveMostRelevantDocument(
                    queryEmbedding.getOutput().getEmbeddings().get(0).getEmbedding());

            System.out.println("最相关的文档: " + mostRelevantDoc);
            // 3. 构建提示词，结合检索到的文档和用户问题
            String prompt = buildRagPrompt(question, mostRelevantDoc);

            // 4. 调用语言模型生成回答
            String responseText = model.generate(prompt);

            System.out.println(responseText);
            return responseText;
        } catch (Exception e) {
            e.printStackTrace();
            return "处理请求时出错: " + e.getMessage();
        }
    }

    /**
     * 检索最相关的文档
     * @param queryVector
     * @return
     */
    private String retrieveMostRelevantDocument(List<Double> queryVector) {
        // 简化版检索，实际应用中应使用向量数据库如Milvus、FAISS等
        double maxSimilarity = -1;
        String mostRelevantDoc = "";

        for (String doc : knowledgeDocuments) {
            try {
                TextEmbeddingParam param = TextEmbeddingParam
                        .builder()
                        .model(TextEmbedding.Models.TEXT_EMBEDDING_V3)
                        .texts(Arrays.asList(doc)).build();
                TextEmbedding textEmbedding = new TextEmbedding();
                TextEmbeddingResult docEmbedding = textEmbedding.call(param);

                List<Double> docVector = docEmbedding.getOutput().getEmbeddings().get(0).getEmbedding();
                double similarity = cosineSimilarity(queryVector, docVector);

                if (similarity > maxSimilarity) {
                    maxSimilarity = similarity;
                    mostRelevantDoc = doc;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return mostRelevantDoc;
    }

    /**
     * 计算两个向量的余弦相似度
     * @param vectorA
     * @param vectorB
     * @return
     */
    private double cosineSimilarity(List<Double> vectorA, List<Double> vectorB) {
        // 计算两个向量的余弦相似度
        double dotProduct = 0.0;
        double normA = 0.0;
        double normB = 0.0;

        for (int i = 0; i < vectorA.size(); i++) {
            dotProduct += vectorA.get(i) * vectorB.get(i);
            normA += Math.pow(vectorA.get(i), 2);
            normB += Math.pow(vectorB.get(i), 2);
        }

        return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
    }

    /**
     * 构建RAG提示词
     * @param question
     * @param context
     * @return
     */
    private String buildRagPrompt(String question, String context) {
        return String.format(
                "基于以下上下文信息回答问题。如果上下文不包含答案，请回答你不知道。\n\n" +
                        "上下文：%s\n\n" +
                        "问题：%s\n\n" +
                        "回答：", context, question);
    }

}

//    private String retrieveMostRelevantDocument(List<Double> queryVector) {
//        double maxSimilarity = -1;
//        String mostRelevantDoc = "";
//
//        for (String doc : knowledgeDocuments) {
//            try {
//                EmbeddingResult docEmbedding = embedding.call(
//                        new EmbeddingParam()
//                                .setModel(TextEmbedding.Models.TEXT_EMBEDDING_V2)
//                                .setTexts(List.of(doc)));
//
//                // 转换为List<Float>
//                List<Float> docVector = docEmbedding.getOutput().getEmbeddings().get(0)
//                        .stream()
//                        .map(Double::floatValue)
//                        .collect(Collectors.toList());
//
//                // 将queryVector也转换为Float
//                List<Float> floatQueryVector = queryVector.stream()
//                        .map(Double::floatValue)
//                        .collect(Collectors.toList());
//
//                double similarity = cosineSimilarity(floatQueryVector, docVector);
//
//                if (similarity > maxSimilarity) {
//                    maxSimilarity = similarity;
//                    mostRelevantDoc = doc;
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }
//
//        return mostRelevantDoc;
//    }
